home *** CD-ROM | disk | FTP | other *** search
- /*
- * refclock_pst - driver for the PSTI 1010/1020 WWV clock
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <sys/time.h>
- #include <sys/file.h>
- #include <sys/ioctl.h>
- #include <sgtty.h>
-
- #include "ntp_syslog.h"
- #include "ntp_fp.h"
- #include "ntp.h"
- #include "ntp_refclock.h"
- #include "ntp_unixtime.h"
-
- #if defined(REFCLOCK) && defined(PST)
- /*
- * This driver is in good measure due to David Schachter, who wrote
- * the firmware for the PST clock. Not that he is to blame for
- * any of this, but he kindly loaned me a clock to allow me to
- * debug this.
- *
- * Postscript:
- *
- * The strategy in here is actually pretty good, especially if
- * you try to support the clock on something lacking low order
- * clock bits like a Sun, since all the business which is done
- * before taking a time stamp tends to randomize the taking of
- * the stamp with respect to the timer interrupt. It is, however,
- * a big cpu hog, and in some ways is a bit of a waste since, as
- * it turns out, the PST clock can give you no better than a
- * millisecond precision and it doesn't pay to try to push it
- * harder.
- *
- * In any event, like the first waffle off the iron, this one
- * should probably be tossed. My current preference would be
- * to retain the 12-a-minute schedule, but to use the QU command
- * instead of the QD and QT, and to only send a QM command with
- * the 12th poll of the minute to get the minutes-since-sync
- * and the station. Need to get a clock which supports QU,
- * however.
- *
- * End postscript
- *
- * This driver polls the clock using the QM, QT and QD commands.
- * Ntpd actually uses QU instead of the last two, something I would
- * like to have done as well since it gives you the day and time
- * atom, but the firmware in the clock I had (X04.01.999) didn't know
- * about this command.
- *
- * The QM command produces output like:
- *
- * O6B532352823C00270322
- * b c deeee
- *
- * We use (b) for the time zone, (c) to see whether time is available,
- * (d) to tell whether we are sync'd to WWV or WWVH, and (e) to determine
- * the number of minutes since the last signal was received. We
- * don't trust the clock for more than about 20 minutes on its own.
- * After this, we keep taking the time but mark the clock unsynchronized.
- *
- * The QT command returns something that looks like this:
- *
- * 18:57:50.263D
- *
- * Note that this particular sample is in 24 hour format, local time
- * (daylight savings time even). We allow just about anything for
- * this (sigh) since this leaves the clock owner free to set the
- * display mode in whatever way he finds convenient for setting
- * his watch.
- *
- * The QD command returns:
- *
- * 89/10/19/292
- *
- * We actually only use the day-of-the-year here. We use the year
- * only to determine whether the PST clock thinks the current year
- * has 365 or 366 days in it.
- *
- * At the current writing, this code expects to be using a BSD-style
- * terminal driver. It will compile code which uses the CLKLDISC
- * line discipline if it thinks this is available, but use cooked
- * mode otherwise. The cooked mode stuff may not have been tested.
- */
-
- /*
- * Definitions
- */
- #define MAXUNITS 4 /* maximum number of PST units permitted */
- #define PSTDEV "/dev/pst%d" /* device we open. %d is unit number */
- #define NPSTSAMPS 12 /* take 12 PST samples per minute */
-
- /*
- * When the PST clock is operating optimally we want the primary clock
- * distance to come out at 300 ms. Thus, peer.distance in the PST peer
- * structure is set to 290 ms and we compute delays which are at least
- * 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
- */
- #define PSTDISTANCE 0x00004a3d
- #define PSTBASEDELAY 0x0000028f
-
- /*
- * Other constant stuff
- */
- #define PSTPRECISION (-9) /* what the heck */
- #define WWVREFID "WWV\0"
- #define WWVHREFID "WWVH"
- #define PSTHSREFID 0x7f7f030a /* 127.127.3.10 refid for hi strata */
-
- /*
- * Parameters for the clock
- */
- #define PSTBAUD B9600
- #ifdef CLKLDISC
- #define PSTMAGIC1 '\r'
- #define PSTMAGIC2 ('\r' | 0x80)
- #define PSTEOL '\r'
- #else
- #define PSTEOL '\n'
- #endif
-
- /*
- * Description of clock. We fill in whether it is a 1010 or 1020,
- * and the firmware revision, using the QV command.
- */
- #define PSTDESCLEN 64
- #define PSTDESCRIPTION "%s %s (%s) WWV/H Receiver"
- #define PSTDEFDESC "PSTI/Traconex 10?0 (V??.??) WWV/H Receiver"
-
- /*
- * Length of the PST time code. This must be the length of the output
- * of the QM command, plus QT, plus QD, plus two spaces. We make it
- * big just on principle.
- */
- #define PSTCODELEN (128)
-
- /*
- * Minimum and maximum lengths
- */
- #define PSTMINQVLEN (16)
- #define PSTMAXQVLEN (24)
-
- #define PSTMINQMLEN (19)
- #define PSTMAXQMLEN (32)
-
- #define PSTMINQDLEN (12)
- #define PSTMAXQDLEN (12)
-
- #define PSTMINQTLEN (14)
- #define PSTMAXQTLEN (14)
-
- /*
- * It turns out that the QT command does *not* adjust for transmission
- * delays. Since the QT command returns 15 characters at 9600 baud,
- * the adjustment for this should be 15.6 ms. We'll default to this,
- * but don't let this stop you from fiddling with the fudge factors
- * to make things come out right
- */
- #define PSTQTFUDGE 0x04000000 /* about 15.6 ms */
-
- /*
- * Default propagation delays. About right for Toronto
- */
- #define DEFWWVPROP 0x01eb851f /* about 7.5 ms */
- #define DEFWWVHPROP 0x06c8b439 /* about 26.5 ms */
-
- /*
- * Maximum propagation delay we believe. 125 ms as an l_fp fraction
- */
- #define PSTMAXPROP 0x20000000
-
- /*
- * Default minutes since an update.
- */
- #define DEFMAXFREERUN (20)
-
- /*
- * Hold time after a leap second occurs
- */
- #define PSTLEAPHOLD (3*60*60) /* 3 hours */
-
- /*
- * Hack to avoid excercising the multiplier. I have no pride.
- */
- #define MULBY10(x) (((x)<<3) + ((x)<<1))
-
- /*
- * PST unit control structure.
- */
- struct pstunit {
- struct peer *peer; /* associated peer structure */
- struct event psttimer; /* timeout timer structure */
- struct refclockio pstio; /* given to the I/O handler */
- l_fp rectimes[NPSTSAMPS]; /* times we received this stuff */
- l_fp reftimes[NPSTSAMPS]; /* times of codes received */
- l_fp lastrec; /* last receive time */
- l_fp lastref; /* last reference time */
- char description[PSTDESCLEN]; /* description of clock */
- char lastcode[PSTCODELEN]; /* last code we received */
- u_char lencode; /* length of the last code */
- u_char nextsample; /* the next offset expected */
- u_char unit; /* unit number for this guy */
- u_char state; /* what we're waiting for */
- s_char station; /* WWV or WWVH? */
- u_char dontsync; /* something detected to prevent sync */
- u_char flags; /* flag byte */
- u_char status; /* clock status */
- u_char lastevent; /* last clock event */
- u_char timezone; /* hour offset to time zone */
- u_char errors; /* number of errors detected */
- u_char year; /* year reported by clock */
- u_char month; /* month, from clock */
- u_char monthday; /* day, from clock */
- u_char hour; /* hour of day */
- u_char minute; /* minute of day */
- u_char second; /* second of day */
- s_char tzoffset; /* time zone offset */
- u_char reason; /* reason for failure */
- u_short millisecond; /* millisecond of day */
- u_short yearday; /* day of the year */
- u_short timesincesync; /* time since radio got sample */
- u_long yearstart; /* NTP time at year start */
- u_long leapend; /* time of ending of leap event */
- u_long lastupdate; /* last time data received */
- u_long polls; /* number of polls */
- u_long noreply; /* number of time outs */
- u_long badformat; /* number of bad format responses */
- u_long baddata; /* number of invalid time codes */
- u_long timestarted; /* time we started this */
- };
-
- /*
- * States we might be in
- */
- #define STATE_IDLE 0 /* not doing anything in particular */
- #define STATE_QV 1 /* trying to get version */
- #define STATE_QM 2 /* sent QM */
- #define STATE_QD 3 /* sent QD */
- #define STATE_QT 4 /* send QT */
-
- /*
- * Status flags
- */
- #define PST_LEAPYEAR 0x1 /* pst clock thinks it is a leap year */
- #define PST_SIGFAULT 0x2 /* signal fault */
- #define PST_HARDERR 0x4 /* hardware error */
- #define PST_NOTIME 0x8 /* no time available */
- #define PST_WWVH 0x10 /* synchronized to WWVH */
- #define PST_DOQV 0x20 /* get version, reinit delays */
- #define PST_DORESET 0x40 /* reset the clock */
-
- /*
- * The PST often encodes stuff by adding an ASCII '0' to it. The
- * largest range of values encoded this way is 0 through 31, or '0'
- * through 'O'. These macroes manipulate these values.
- */
- #define ISVALIDPST(c) ((c) >= '0' && (c) <= 'O')
- #define PSTTOBIN(c) ((int)(c) - '0')
- #define BINTOPST(c) ((char)((c) + '0'))
-
- /*
- * Status bits. Look at the QM command
- */
- #define SIGFAULT 0x1
- #define HARDFAULT 0x2
- #define OUTOFSPEC 0x4
- #define TIMEAVAILABLE 0x8
-
- /*
- * Module reason codes
- */
- #define QVREASON 20
- #define QMREASON 40
- #define QDREASON 60
- #define QTREASON 80
-
- /*
- * Station i.d. characters in QM output
- */
- #define WWV_CHAR 'C'
- #define WWVH_CHAR 'H'
-
- /*
- * We allow a few errors, but if we get more than 12 seconds behind
- * the schedule we start from sample 0 again. 4 seconds is the minimum
- * time between time out routine executions.
- */
- #define PSTMAXDELAY 12
- #define PSTMINTIMEOUT 4
-
- /*
- * The PST polling schedule. We poll 12 times per 64 seconds (far too
- * many, but what the heck). The polls are scheduled to finish in this
- * time with the assumption that the timer is good for no better than
- * 4 second resolution. If we get too far behind (due to bad samples
- * or no responses) we start over.
- */
- struct pstsched {
- u_short nextinterval;
- u_short tooold;
- };
-
- static struct pstsched psttab[NPSTSAMPS] = {
- { 4, PSTMAXDELAY+1 },
- { 4, PSTMAXDELAY+1+4 },
- { 8, PSTMAXDELAY+1+4+4 },
- { 4, PSTMAXDELAY+1+4+4+8 },
- { 8, PSTMAXDELAY+1+4+4+8+4 },
- { 4, PSTMAXDELAY+1+4+4+8+4+8 },
- { 4, PSTMAXDELAY+1+4+4+8+4+8+4 },
- { 8, PSTMAXDELAY+1+4+4+8+4+8+4+4 },
- { 4, PSTMAXDELAY+1+4+4+8+4+8+4+4+8 },
- { 8, PSTMAXDELAY+1+4+4+8+4+8+4+4+8+4 },
- { 4, PSTMAXDELAY+1+4+4+8+4+8+4+4+8+4+8 },
- { 4, PSTMAXDELAY+1+4+4+8+4+8+4+4+8+4+8+4 }
- };
-
-
- /*
- * Data space for the unit structures. Note that we allocate these on
- * the fly, but never give them back.
- */
- static struct pstunit *pstunits[MAXUNITS];
- static u_char unitinuse[MAXUNITS];
-
- /*
- * Structure to keep processed propagation data in.
- */
- struct pst_propagate {
- u_long remainder; /* left over submillisecond remainder */
- char msbchar; /* character for high order bits */
- char lsbchar; /* character for low order bits */
- };
-
-
- /*
- * Keep the fudge factors separately so they can be set even
- * when no clock is configured.
- */
- static l_fp wwv_prop_delay[MAXUNITS];
- static l_fp wwvh_prop_delay[MAXUNITS];
- static struct pst_propagate wwv_prop_data[MAXUNITS];
- static struct pst_propagate wwvh_prop_data[MAXUNITS];
- static u_char stratumtouse[MAXUNITS];
- static u_char sloppyclock[MAXUNITS];
- static u_short freerun[MAXUNITS];
-
- /*
- * Pointer to the default description
- */
- static char *pstdefdesc = PSTDEFDESC;
-
- /*
- * macro for writing to the clock, printing an error if we fail
- */
- #define pst_send(pst, str, len) \
- if (write((pst)->pstio.fd, (str), (len)) < 0) \
- pst_write_error((pst))
-
- /*
- * macro for resetting the clock structure to zero
- */
- #define pst_reset(pst) \
- do { \
- pst->nextsample = 0; \
- pst->station = 0; \
- pst->dontsync = 0; \
- } while (0)
-
- /*
- * macro for event reporting
- */
- #define pst_event(pst, evnt_code) \
- do { \
- if ((pst)->status != (u_char)(evnt_code)) \
- pst_do_event((pst), (evnt_code)); \
- } while (0)
-
- /*
- * Imported from the timer module
- */
- extern u_long current_time;
- extern struct event timerqueue[];
-
- /*
- * Time conversion tables imported from the library
- */
- extern u_long msutotsflo[];
- extern u_long msutotsfhi[];
-
-
- /*
- * pst_init - initialize internal PST driver data
- */
- void
- pst_init()
- {
- register int i;
- void pst_compute_delay();
-
- /*
- * Just zero the data arrays
- */
- bzero((char *)pstunits, sizeof pstunits);
- bzero((char *)unitinuse, sizeof unitinuse);
-
- /*
- * Initialize fudge factors to default.
- */
- for (i = 0; i < MAXUNITS; i++) {
- wwv_prop_delay[i].l_ui = 0;
- wwv_prop_delay[i].l_uf = DEFWWVPROP;
- pst_compute_delay(DEFWWVPROP, &wwv_prop_data[i]);
- wwvh_prop_delay[i].l_ui = 0;
- wwvh_prop_delay[i].l_uf = DEFWWVHPROP;
- pst_compute_delay(DEFWWVHPROP, &wwvh_prop_data[i]);
- stratumtouse[i] = 0;
- sloppyclock[i] = 0;
- freerun[i] = DEFMAXFREERUN;
- }
- }
-
-
- /*
- * pst_start - open the PST device and initialize data for processing
- */
- int
- pst_start(unit, peer)
- u_int unit;
- struct peer *peer;
- {
- register struct pstunit *pst;
- register int i;
- int fd;
- int ldisc;
- char pstdev[20];
- struct sgttyb ttyb;
- void pst_timeout();
- void pst_receive();
- extern int io_addclock();
- extern char *emalloc();
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "pst clock: unit number %d invalid (max %d)",
- unit, MAXUNITS-1);
- return 0;
- }
- if (unitinuse[unit]) {
- syslog(LOG_ERR, "pst clock: unit number %d in use", unit);
- return 0;
- }
-
- /*
- * Unit okay, attempt to open the device.
- */
- (void) sprintf(pstdev, PSTDEV, unit);
-
- fd = open(pstdev, O_RDWR, 0777);
- if (fd == -1) {
- syslog(LOG_ERR, "pst clock: open of %s failed: %m", pstdev);
- return 0;
- }
-
- /*
- * Set for exclusive use
- */
- if (ioctl(fd, TIOCEXCL, (char *)0) < 0) {
- syslog(LOG_ERR, "pst clock: ioctl(%s, TIOCEXCL): %m", pstdev);
- (void) close(fd);
- return 0;
- }
-
- /*
- * Set to raw mode
- */
- ttyb.sg_ispeed = ttyb.sg_ospeed = PSTBAUD;
- #ifdef CLKLDISC
- ttyb.sg_erase = PSTMAGIC1;
- ttyb.sg_kill = PSTMAGIC2;
- ttyb.sg_flags = EVENP|ODDP|RAW|CRMOD;
- #else
- ttyb.sg_erase = ttyb.sg_kill = 0;
- ttyb.sg_flags = EVENP|ODDP|CRMOD;
- #endif
- if (ioctl(fd, TIOCSETP, (char *)&ttyb) < 0) {
- syslog(LOG_ERR, "pst clock: ioctl(%s, TIOCSETP): %m", pstdev);
- return 0;
- }
-
- /*
- * Looks like this might succeed. Find memory for the structure.
- * Look to see if there are any unused ones, if not we malloc()
- * one.
- */
- if (pstunits[unit] != 0) {
- pst = pstunits[unit]; /* The one we want is okay */
- } else {
- for (i = 0; i < MAXUNITS; i++) {
- if (!unitinuse[i] && pstunits[i] != 0)
- break;
- }
- if (i < MAXUNITS) {
- /*
- * Reclaim this one
- */
- pst = pstunits[i];
- pstunits[i] = 0;
- } else {
- pst = (struct pstunit *)emalloc(sizeof(struct pstunit));
- }
- }
- bzero((char *)pst, sizeof(struct pstunit));
- pstunits[unit] = pst;
-
- /*
- * Set up the structure
- */
- pst->peer = peer;
- pst->unit = (u_char)unit;
- pst->state = STATE_IDLE;
- pst->flags |= PST_DOQV;
- pst->timestarted = current_time;
- (void) strcpy(pst->description, pstdefdesc);
-
- pst->psttimer.peer = (struct peer *)pst;
- pst->psttimer.event_handler = pst_timeout;
-
- pst->pstio.clock_recv = pst_receive;
- pst->pstio.srcclock = (caddr_t)pst;
- pst->pstio.datalen = 0;
- pst->pstio.fd = fd;
-
- /*
- * Okay. Set the line discipline to the clock line discipline,
- * if we have it, then give it to the I/O code to start receiving
- * stuff.
- */
- #ifdef CLKLDISC
- ldisc = CLKLDISC;
- if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) {
- syslog(LOG_ERR, "pst clock: ioctl(%s, TIOCSETD): %m", pstdev);
- (void) close(fd);
- return 0;
- }
- #else
- ldisc = 0;
- if (ioctl(fd, TIOCFLUSH, (char *)&ldisc) < 0) {
- syslog(LOG_ERR, "pst clock: ioctl(%s, TIOCFLUSH): %m", pstdev);
- (void) close(fd);
- return 0;
- }
- #endif
- if (!io_addclock(&pst->pstio)) {
- /*
- * Oh shit. Just close and return.
- */
- (void) close(fd);
- return 0;
- }
-
- /*
- * All done. Initialize a few random peer variables, then
- * start the timer and return success.
- */
- peer->distance = PSTDISTANCE;
- peer->precision = PSTPRECISION;
- peer->stratum = stratumtouse[unit];
- if (stratumtouse[unit] <= 1)
- bcopy(WWVREFID, (char *)&peer->refid, 4);
- else
- peer->refid = htonl(PSTHSREFID);
- pst->psttimer.event_time = current_time + PSTMINTIMEOUT;
- TIMER_ENQUEUE(timerqueue, &pst->psttimer);
- unitinuse[unit] = 1;
- return 1;
- }
-
-
- /*
- * pst_shutdown - shut down a PST clock
- */
- void
- pst_shutdown(unit)
- int unit;
- {
- register struct pstunit *pst;
- extern void io_closeclock();
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR,
- "pst clock: INTERNAL ERROR, unit number %d invalid (max %d)",
- unit, MAXUNITS-1);
- return;
- }
- if (!unitinuse[unit]) {
- syslog(LOG_ERR,
- "pst clock: INTERNAL ERROR, unit number %d not in use", unit);
- return;
- }
-
- /*
- * Tell the I/O module to turn us off, and dequeue timer
- * if any. We're history.
- */
- pst = pstunits[unit];
- TIMER_DEQUEUE(&pst->psttimer);
- io_closeclock(&pst->pstio);
- unitinuse[unit] = 0;
- }
-
-
- /*
- * pst_write_error - complain about writes to the clock
- */
- static void
- pst_write_error(pst)
- struct pstunit *pst;
- {
- /*
- * This will fill syslog is something is really wrong. Should
- * throttle it back.
- */
- syslog(LOG_ERR, "pst clock: write error on unit %d: %m",
- pst->unit);
- }
-
-
- /*
- * pst_timeout - process a timeout event
- */
- void
- pst_timeout(fakepeer)
- struct peer *fakepeer;
- {
- register struct pstunit *pst;
- u_long poll;
-
- /*
- * The timeout routine always initiates a chain of
- * query-responses from the clock, by sending either
- * a QV command (if we need to (re)set the propagation
- * delays into the clock), a QM command or an SRY
- * command (after a leap second). The pst_receive()
- * routine should complete the set of queries on its own
- * long before the next time out is due, so if we see any
- * state in here other than idle it means the clock hasn't
- * responded.
- */
- pst = (struct pstunit *)fakepeer;
- switch(pst->state) {
- case STATE_IDLE:
- poll = (u_long)psttab[pst->nextsample].nextinterval;
- break; /* all is well */
-
- case STATE_QV:
- pst->flags |= PST_DOQV; /* no response, do QV again */
- /*FALLSTHROUGH*/
-
- case STATE_QM:
- case STATE_QD:
- case STATE_QT:
- pst->noreply++; /* mark the lack of response */
- poll = PSTMINTIMEOUT; /* minimum time poll */
- break;
-
- default:
- syslog(LOG_ERR,
- "pst clock: INTERNAL ERROR unit %d invalid state %d",
- pst->unit, pst->state);
- poll = PSTMINTIMEOUT; /* minimum time poll */
- break;
- }
-
- if (pst->flags & PST_DORESET) {
- /*
- * Do a reset. At the next interrupt, start with
- * a QV command to set in the delays.
- */
- pst->flags &= ~PST_DORESET;
- pst->flags |= PST_DOQV;
- pst->state = STATE_IDLE;
- pst_send(pst, "\003SRY", 4);
- } else if (pst->flags & PST_DOQV) {
- pst->polls++;
- pst->flags &= ~PST_DOQV;
- pst->state = STATE_QV;
- pst_send(pst, "\003QV", 3);
- } else {
- pst->polls++;
- pst->state = STATE_QM;
- pst_send(pst, "\003QM", 3);
- }
-
- pst->psttimer.event_time += poll;
- TIMER_ENQUEUE(timerqueue, &pst->psttimer);
- }
-
-
- /*
- * pst_QV_process - decode the results of a QV poll and insert fudge
- * factors into the clock.
- */
- static int
- pst_QV_process(pst, rbufp)
- register struct pstunit *pst;
- struct recvbuf *rbufp;
- {
- register char *cp;
- register char *bp;
- register int len;
- char *model;
- char *company;
- char buf[20];
- static char wwvdelay[6] = { 'S', 'C', '\0', 'S', 'E', '\0' };
- static char wwvhdelay[6] = { 'S', 'H', '\0', 'S', 'G', '\0' };
-
- /*
- * The output of the QV command looks like:
- *
- * PSTI ITS V04.01.000\r
- *
- * or
- *
- * TRAC ITS V04.01.000\r
- *
- * The minimum length of the string is about 16 characters.
- * The maximum length is sort of unbounded, but we get suspicious
- * if it is more than 34.
- */
- len = rbufp->recv_length;
- if (len > PSTMAXQVLEN + 10)
- len = PSTMAXQVLEN + 10;
-
- bp = rbufp->recv_buffer;
- cp = pst->lastcode;
- while (len-- > 0) {
- *cp = (*bp++) & 0x7f; /* strip parity */
- if (!isprint(*cp))
- break;
- cp++;
- }
- pst->lencode = (u_char)(cp - pst->lastcode);
-
- /*
- * Okay, got all printable characters from the string
- * copied. We expect to have been terminated by the
- * EOL character. If not, forget it. If the length
- * is insane, forget it.
- */
- if (len < 0 || *cp != PSTEOL
- || pst->lencode < PSTMINQVLEN || pst->lencode > PSTMAXQVLEN) {
- pst->reason = QVREASON + 1;
- return 0;
- }
-
- /*
- * Now, format check what we can. Dump it at the least
- * sign of trouble.
- */
- cp = pst->lastcode;
- if (*cp++ != 'P' || *cp++ != 'S' || *cp++ != 'T'
- || *cp++ != 'I' || *cp++ != ' ') {
- cp = pst->lastcode;
- if (*cp++ != 'T' || *cp++ != 'R' || *cp++ != 'A'
- || *cp++ != 'C' || *cp++ != ' ') {
- pst->reason = QVREASON + 2;
- return 0;
- }
- company = "Traconex";
- } else {
- company = "Precision Standard Time";
- }
-
- if (*cp == 'M')
- model = "1010";
- else if (*cp == 'I')
- model = "1020";
- else {
- pst->reason = QVREASON + 3;
- return 0;
- }
- cp++;
-
- if (*cp++ != 'T' || *cp++ != 'S' || *cp++ != ' ') {
- pst->reason = QVREASON + 4;
- return 0;
- }
- if (*cp != 'X' && *cp != 'V') {
- pst->reason = QVREASON + 5;
- return 0;
- }
-
- /*
- * Next is the version. Copy it into the buffer.
- */
- bp = buf;
- *bp++ = *cp++;
- while (isdigit(*cp) || *cp == '.')
- *bp++ = *cp++;
- *bp++ = '\0';
-
- /*
- * Final bit of fluff is to set the description
- */
- (void) sprintf(pst->description, PSTDESCRIPTION, company, model, buf);
-
- /*
- * Now the serious stuff. Since we are now sure that the
- * clock is there, we can be fairly sure that the delay
- * setting commands will take. Send them.
- */
- wwvdelay[2] = wwv_prop_data[pst->unit].msbchar;
- wwvdelay[5] = wwv_prop_data[pst->unit].lsbchar;
- pst_send(pst, wwvdelay, 6);
-
- /*
- * Same thing for WWVH
- */
- wwvhdelay[2] = wwvh_prop_data[pst->unit].msbchar;
- wwvhdelay[5] = wwvh_prop_data[pst->unit].lsbchar;
- pst_send(pst, wwvhdelay, 6);
-
- /*
- * Should be okay. Return positive response.
- */
- return 1;
- }
-
-
- /*
- * pst_QM_process - process the output of a QM command
- */
- static int
- pst_QM_process(pst, rbufp)
- register struct pstunit *pst;
- struct recvbuf *rbufp;
- {
- register char *cp;
- register char *bp;
- register int n;
-
- /*
- * The output of the QM command looks like:
- *
- * O6B532352823C00270322
- *
- * The minimum length of the string is 19 characters.
- * The maximum length is sort of unbounded, but we get suspicious
- * if it is more than 42.
- */
- n = rbufp->recv_length;
- if (n > PSTMAXQMLEN + 10)
- n = PSTMAXQMLEN + 10;
-
- bp = rbufp->recv_buffer;
- cp = pst->lastcode;
- while (n-- > 0) {
- *cp = (*bp++) & 0x7f; /* strip parity */
- if (!isprint(*cp))
- break;
- cp++;
- }
- pst->lencode = (u_char)(cp - pst->lastcode);
-
- /*
- * Okay, got all printable characters from the string
- * copied. We expect to have been terminated by the
- * EOL character. If not, forget it. If the length
- * is insane, forget it.
- */
- if (n < 0 || *cp != PSTEOL
- || pst->lencode < PSTMINQMLEN || pst->lencode > PSTMAXQMLEN) {
- pst->reason = QMREASON + 1;
- return 0;
- }
-
- /*
- * Ensure that the first PSTMINQMLEN characters are valid with
- * respect to the way the clock encodes binary data.
- */
- cp = pst->lastcode;
- n = pst->lencode;
- while (n-- > 0) {
- if (!ISVALIDPST(*cp)) {
- pst->reason = QMREASON + 2;
- return 0;
- }
- cp++;
- }
-
- /*
- * Collect information we are interested in.
- */
- cp = pst->lastcode;
- pst->timezone = PSTTOBIN(cp[3]);
- if (pst->timezone > 23) {
- pst->reason = QMREASON + 3;
- return 0;
- }
-
- pst->flags &=
- ~(PST_LEAPYEAR|PST_SIGFAULT|PST_HARDERR|PST_NOTIME|PST_WWVH);
- n = PSTTOBIN(cp[4]);
- if (n > 15) {
- pst->reason = QMREASON + 4;
- return 0;
- }
- if (((n + 2) & 0x3) == 0)
- pst->flags |= PST_LEAPYEAR;
-
- n = PSTTOBIN(cp[9]);
- if (n > 15) {
- pst->reason = QMREASON + 5;
- return 0;
- }
- if (n & SIGFAULT)
- pst->flags |= PST_SIGFAULT;
- if (n & HARDFAULT)
- pst->flags |= PST_HARDERR;
- if (!(n & TIMEAVAILABLE))
- pst->flags |= PST_NOTIME;
-
- if (cp[12] == 'H') {
- pst->flags |= PST_WWVH;
- } else if (cp[12] == 'C') {
- pst->flags &= ~PST_WWVH;
- } else {
- pst->reason = QMREASON + 6;
- return 0;
- }
-
- if (wwv_prop_data[pst->unit].msbchar != cp[5] ||
- wwv_prop_data[pst->unit].lsbchar != cp[6] ||
- wwvh_prop_data[pst->unit].msbchar != cp[7] ||
- wwvh_prop_data[pst->unit].lsbchar != cp[8])
- pst->flags |= PST_DOQV;
-
- bp = cp + 13;
- pst->timesincesync = 0;
- while (bp < (cp + 17)) {
- if (!isdigit(*bp)) {
- pst->reason = QMREASON + 6;
- return 0;
- }
- pst->timesincesync = MULBY10(pst->timesincesync)
- + PSTTOBIN(*bp);
- bp++;
- }
-
- /*
- * That's about all we can do. Return success.
- */
- return 1;
- }
-
-
- /*
- * pst_QD_process - process the output of a QD command
- */
- static int
- pst_QD_process(pst, rbufp)
- register struct pstunit *pst;
- struct recvbuf *rbufp;
- {
- register char *cp;
- register char *bp;
- register int n;
- char *cpstart;
- int len;
-
- /*
- * The output of the QM command looks like:
- *
- * 88/05/17/138\r
- *
- * The minimum length of the string is 12 characters as is
- * the maximum length.
- */
- n = rbufp->recv_length;
- if (n > PSTMAXQDLEN + 10)
- n = PSTMAXQDLEN + 10;
-
- bp = rbufp->recv_buffer;
- cp = &pst->lastcode[pst->lencode];
- *cp++ = ' ';
- cpstart = cp;
- while (n-- > 0) {
- *cp = (*bp++) & 0x7f; /* strip parity */
- if (!isprint(*cp))
- break;
- cp++;
- }
- len = (cp - cpstart);
- pst->lencode = (u_char)(cp - pst->lastcode);
-
- /*
- * Okay, got all printable characters from the string
- * copied. We expect to have been terminated by the
- * EOL character. If not, forget it. If the length
- * is insane, forget it.
- */
- if (n < 0 || *cp != PSTEOL ||
- len < PSTMINQDLEN || len > PSTMAXQDLEN) {
- pst->reason = QDREASON + 1;
- return 0;
- }
-
- /*
- * Ensure that the characters are formatted validly. They
- * are either digits or '/'s.
- */
- cp = cpstart;
- if (!isdigit(cp[0]) || !isdigit(cp[1]) || cp[2] != '/' ||
- !isdigit(cp[3]) || !isdigit(cp[4]) || cp[5] != '/' ||
- !isdigit(cp[6]) || !isdigit(cp[7]) || cp[8] != '/' ||
- !isdigit(cp[9]) || !isdigit(cp[10]) || !isdigit(cp[11])) {
- pst->reason = QDREASON + 2;
- return 0;
- }
-
- /*
- * Decode into year, month, day and year day
- */
- pst->year = MULBY10(PSTTOBIN(cp[0])) + PSTTOBIN(cp[1]);
- pst->month = MULBY10(PSTTOBIN(cp[3])) + PSTTOBIN(cp[4]);
- pst->monthday = MULBY10(PSTTOBIN(cp[6])) + PSTTOBIN(cp[7]);
- pst->yearday = MULBY10(PSTTOBIN(cp[9])) + PSTTOBIN(cp[10]);
- pst->yearday = MULBY10(pst->yearday) + PSTTOBIN(cp[11]);
-
- /*
- * Format check these.
- */
- if (pst->month > 12 || pst->monthday > 31 || pst->yearday > 366) {
- pst->reason = QDREASON + 3;
- return 0;
- }
- if (!(pst->flags & PST_LEAPYEAR) && pst->yearday > 365) {
- pst->reason = QDREASON + 4;
- return 0;
- }
-
- /*
- * Done all we can.
- */
- return 1;
- }
-
-
- /*
- * pst_QT_process - process the output of a QT command, return the times
- */
- static int
- pst_QT_process(pst, rbufp, tsclk, tsrec)
- register struct pstunit *pst;
- struct recvbuf *rbufp;
- l_fp *tsclk;
- l_fp *tsrec;
- {
- register char *cp;
- register char *bp;
- register int n;
- char *cpstart;
- int len;
- int hour;
- int minute;
- int second;
- int msec;
- int tzoff;
- extern int buftvtots();
-
- /*
- * The output of the QT command looks like:
- *
- * A09:57:50.263D
- *
- * The minimum length of the string is 14 characters as is
- * the maximum length.
- */
- n = rbufp->recv_length;
- if (n > PSTMAXQTLEN + 10)
- n = PSTMAXQTLEN + 10;
-
- bp = rbufp->recv_buffer;
- cp = &pst->lastcode[pst->lencode];
- *cp++ = ' ';
- cpstart = cp;
- while (n-- > 0) {
- *cp = (*bp++) & 0x7f; /* strip parity */
- if (!isprint(*cp))
- break;
- cp++;
- }
- len = (cp - cpstart);
- pst->lencode = (u_char)(cp - pst->lastcode);
-
- /*
- * Okay, got all printable characters from the string
- * copied. We expect to have been terminated by the
- * EOL character. If not, forget it. If the length
- * is insane, forget it.
- */
- if (n < 0 || *cp != PSTEOL ||
- len < PSTMINQTLEN || len > PSTMAXQTLEN) {
- pst->reason = QTREASON + 1;
- return 0;
- }
- #ifdef CLKLDISC
- /*
- * Receive time stamp should be in buffer after the code.
- * Make sure we have enough characters in there.
- */
- if (&rbufp->recv_buffer[rbufp->recv_length] - bp < 8) {
- pst->reason = QTREASON + 2;
- return 0;
- }
- if (!buftvtots(bp, tsrec)) {
- pst->reason = QTREASON + 3;
- return 0;
- }
- #else
- /*
- * Use the timestamp collected with the input.
- */
- *tsrec = rbufp->recv_time;
- #endif
-
- /*
- * Ensure that the characters are formatted validly. Mostly
- * digits, but the occasional `:' and `.'.
- */
- cp = cpstart;
- if (!isdigit(cp[1]) || !isdigit(cp[2]) || cp[3] != ':' ||
- !isdigit(cp[4]) || !isdigit(cp[5]) || cp[6] != ':' ||
- !isdigit(cp[7]) || !isdigit(cp[8]) || cp[9] != '.' ||
- !isdigit(cp[10]) || !isdigit(cp[11]) || !isdigit(cp[12])) {
- pst->reason = QTREASON + 4;
- return 0;
- }
-
- /*
- * Extract the hour, minute, second and millisecond
- */
- hour = MULBY10(PSTTOBIN(cp[1])) + PSTTOBIN(cp[2]);
- minute = MULBY10(PSTTOBIN(cp[4])) + PSTTOBIN(cp[5]);
- second = MULBY10(PSTTOBIN(cp[7])) + PSTTOBIN(cp[8]);
- msec = MULBY10(PSTTOBIN(cp[10])) + PSTTOBIN(cp[11]);
- msec = MULBY10(msec) + PSTTOBIN(cp[12]);
-
- if (minute > 59 || second > 59) {
- pst->reason = QTREASON + 5;
- return 0;
- }
-
- /*
- * Trouble here. Adjust the hours for AM/PM, if this is
- * on, and for daylight saving time.
- */
- if (*cp == 'A') {
- if (hour > 12 || hour == 0) {
- pst->reason = QTREASON + 5;
- return 0;
- }
- if (hour == 12)
- hour = 0;
- } else if (*cp == 'P') {
- if (hour > 12 || hour == 0)
- return 0;
- if (hour < 12)
- hour += 12;
- } else if (*cp != ' ') {
- pst->reason = QTREASON + 6;
- return 0;
- }
-
- if (cp[13] == 'D')
- tzoff = -1;
- else if (cp[13] == ' ')
- tzoff = 0;
- else {
- pst->reason = QTREASON + 7;
- return 0;
- }
-
- /*
- * Adjust for the timezone. The PST manual is screwy here.
- * it says the timezone is an integer in the range 0 to 23,
- * but this doesn't allow us to tell the difference between
- * +12 and -12. Assume the 12 hour timezone is west of
- * GMT.
- */
- if (pst->timezone <= 12)
- tzoff += pst->timezone;
- else
- tzoff -= (24 - pst->timezone);
-
-
- /*
- * Record for posterity
- */
- pst->hour = (u_char)hour;
- pst->minute = (u_char)minute;
- pst->second = (u_char)second;
- pst->millisecond = (u_short)msec;
- pst->tzoffset = (s_char)tzoff;
-
- /*
- * All that to get the day-hour-minute-second. Turn this
- * into the seconds part of a time stamp. Also use the
- * milliseconds part directly as the fractional part.
- */
- MSUTOTSF(msec, tsclk->l_uf);
- if (!clocktime((int)pst->yearday, hour, minute, second, tzoff,
- tsrec->l_ui, &pst->yearstart, &tsclk->l_ui)) {
- pst->reason = QTREASON + 8;
- return 0;
- }
-
- /*
- * Add in the fudge
- */
- if (pst->flags & PST_WWVH)
- L_ADDUF(tsclk, wwvh_prop_data[pst->unit].remainder);
- else
- L_ADDUF(tsclk, wwv_prop_data[pst->unit].remainder);
-
- /*
- * Glad that's over with
- */
- return 1;
- }
-
-
- /*
- * pst_do_event - update our status and report any changes
- */
- static void
- pst_do_event(pst, evnt_code)
- register struct pstunit *pst;
- int evnt_code;
- {
- if (pst->status != (u_char)evnt_code) {
- pst->status = (u_char)evnt_code;
- if (evnt_code != CEVNT_NOMINAL)
- pst->lastevent = (u_char)evnt_code;
- /*
- * Should trap this, but the trap code isn't up to
- * it yet.
- */
- }
- }
-
-
-
- /*
- * pst_process - process the data collected to produce an offset estimate
- */
- static void
- pst_process(pst)
- register struct pstunit *pst;
- {
- register int i;
- register int n;
- register u_long tmp_ui;
- register u_long tmp_uf;
- register u_long date_ui;
- register u_long date_uf;
- u_fp delay;
- l_fp off[NPSTSAMPS];
- extern void refclock_receive();
-
- /*
- * Compute offsets from the raw data. Sort them into
- * ascending order.
- */
- for (i = 0; i < NPSTSAMPS; i++) {
- tmp_ui = pst->reftimes[i].l_ui;
- tmp_uf = pst->reftimes[i].l_uf;
- M_SUB(tmp_ui, tmp_uf, pst->rectimes[i].l_ui,
- pst->rectimes[i].l_uf);
- for (n = i; n > 0; n--) {
- if (M_ISGEQ(tmp_ui, tmp_uf, off[n-1].l_ui,
- off[n-1].l_uf))
- break;
- off[n] = off[n-1];
- }
- off[n].l_ui = tmp_ui;
- off[n].l_uf = tmp_uf;
- }
-
- /*
- * Reject the furthest from the median until 8 samples left
- */
- i = 0;
- n = NPSTSAMPS;
- while ((n - i) > 8) {
- tmp_ui = off[n-1].l_ui;
- tmp_uf = off[n-1].l_uf;
- date_ui = off[(n+i)/2].l_ui;
- date_uf = off[(n+i)/2].l_uf;
- M_SUB(tmp_ui, tmp_uf, date_ui, date_uf);
- M_SUB(date_ui, date_uf, off[i].l_ui, off[i].l_uf);
- if (M_ISHIS(date_ui, date_uf, tmp_ui, tmp_uf)) {
- /*
- * reject low end
- */
- i++;
- } else {
- /*
- * reject high end
- */
- n--;
- }
- }
-
- /*
- * Compute the delay based on the difference between the
- * extremes of the remaining offsets.
- */
- tmp_ui = off[n-1].l_ui;
- tmp_uf = off[n-1].l_uf;
- M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
- delay = PSTBASEDELAY + MFPTOFP(tmp_ui, tmp_uf);
-
- /*
- * Now compute the offset estimate. If the sloppy clock
- * flag is set, average the remainder, otherwise pick the
- * median.
- */
- if (sloppyclock[pst->unit]) {
- tmp_ui = tmp_uf = 0;
- while (i < n) {
- M_ADD(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
- i++;
- }
- M_RSHIFT(tmp_ui, tmp_uf);
- M_RSHIFT(tmp_ui, tmp_uf);
- M_RSHIFT(tmp_ui, tmp_uf);
- i = 0;
- off[0].l_ui = tmp_ui;
- off[0].l_uf = tmp_uf;
- } else {
- i = (n+i)/2;
- }
-
- /*
- * Add the default PST QT delay into this.
- */
- L_ADDUF(&off[i], PSTQTFUDGE);
-
- /*
- * Set the reference ID to the appropriate station
- */
- if (stratumtouse[pst->unit] <= 1) {
- if (pst->station >= 0)
- bcopy(WWVREFID, (char *)&pst->peer->refid, 4);
- else
- bcopy(WWVHREFID, (char *)&pst->peer->refid, 4);
- }
-
- /*
- * Give the data to the reference clock support code
- */
- refclock_receive(pst->peer, &off[i], delay, &pst->reftimes[NPSTSAMPS-1],
- &pst->rectimes[NPSTSAMPS-1], (pst->dontsync == 0));
-
- /*
- * If the don't-sync flag isn't on, we're nominal.
- */
- if (pst->dontsync == 0)
- pst_event(pst, CEVNT_NOMINAL);
- pst_reset(pst);
- }
-
-
-
- /*
- * pst_receive - receive data from a PST clock, call the appropriate
- * routine to process it, and advance the state.
- */
- void
- pst_receive(rbufp)
- struct recvbuf *rbufp;
- {
- register struct pstunit *pst;
- register u_long tmp;
- void pst_process();
-
- pst = (struct pstunit *)rbufp->recv_srcclock;
-
- /*
- * Process based on the current state.
- */
- switch(pst->state) {
- case STATE_IDLE:
- return; /* Ignore the input */
-
- case STATE_QV:
- if (!pst_QV_process(pst, rbufp)) {
- /*
- * Set the state to idle, but request another
- * QV poll.
- */
- pst->badformat++;
- pst_event(pst, CEVNT_BADREPLY);
- pst->state = STATE_IDLE;
- pst->flags |= PST_DOQV;
- } else {
- /*
- * This went okay. Advance the state to
- * QM and send the request.
- */
- pst->state = STATE_QM;
- pst_send(pst, "QM", 2);
- }
- return;
-
- case STATE_QM:
- if (!pst_QM_process(pst, rbufp)) {
- /*
- * Idle us and note the error
- */
- pst->badformat++;
- pst_event(pst, CEVNT_BADREPLY);
- pst->state = STATE_IDLE;
- return;
- }
- if (pst->flags & PST_NOTIME) {
- /*
- * Here we aren't getting any time because the
- * clock is still searching. Don't bother
- * looking for anything. Remove any leap
- * second hold, however, since this should
- * ensure the clock is sensible.
- */
- pst_event(pst, CEVNT_FAULT);
- pst->state = STATE_IDLE;
- pst->leapend = 0;
- if (pst->nextsample > 0)
- pst_reset(pst); /* Make sure rate low */
- return;
- }
-
- /*
- * Next is QD. Do it.
- */
- pst->state = STATE_QD;
- pst_send(pst, "QD", 2);
- return;
-
- case STATE_QD:
- if (!pst_QD_process(pst, rbufp)) {
- /*
- * Idle us and note the error
- */
- pst->badformat++;
- pst_event(pst, CEVNT_BADDATE);
- pst->state = STATE_IDLE;
- } else {
- /*
- * Last step is QT.
- */
- pst->state = STATE_QT;
- pst_send(pst, "QT", 2);
- }
- return;
-
- case STATE_QT:
- pst->state = STATE_IDLE;
- if (!pst_QT_process(pst, rbufp, &pst->lastref, &pst->lastrec)) {
- /*
- * Note the error
- */
- pst->baddata++;
- pst_event(pst, CEVNT_BADTIME);
- return;
- }
- break;
-
- default:
- syslog(LOG_ERR,
- "pst clock: INTERNAL ERROR invalid state %d, unit %d, in receive",
- pst->state, pst->unit);
- return;
- }
-
-
- /*
- * You may not have noticed this, but the only way we end up
- * out here is if we've completed polling and have a couple of
- * valid time stamps. First see if we should reset the
- * structure.
- */
- if (pst->nextsample > 0) {
- tmp = pst->lastrec.l_ui - pst->rectimes[0].l_ui;
- if (tmp > (u_long)psttab[pst->nextsample].tooold)
- pst_reset(pst);
- }
-
- pst->rectimes[pst->nextsample] = pst->lastrec;
- pst->reftimes[pst->nextsample] = pst->lastref;
- pst->nextsample++;
- if (pst->flags & PST_WWVH)
- pst->station--;
- else
- pst->station++;
-
- if (pst->flags & (PST_SIGFAULT|PST_HARDERR)) {
- pst_event(pst, CEVNT_FAULT);
- pst->dontsync++;
- } else if (pst->timesincesync > freerun[pst->unit]) {
- pst_event(pst, CEVNT_PROP);
- pst->dontsync++;
- } else if (pst->leapend > current_time) {
- pst->dontsync++;
- }
-
- if (pst->nextsample >= NPSTSAMPS)
- pst_process(pst);
- }
-
-
- /*
- * pst_leap - called when a leap second occurs
- */
- void
- pst_leap()
- {
- register int i;
-
- /*
- * This routine should be entered a few seconds after
- * midnight UTC when a leap second occurs. To ensure we
- * don't get foolish time from the clock(s) we reset it
- * (them). We also set a 3 hour hold on each clock in
- * case the reset doesn't take, though this will be canceled
- * if the reset succeeds.
- */
- for (i = 0; i < MAXUNITS; i++) {
- if (unitinuse[i]) {
- pstunits[i]->leapend = current_time + PSTLEAPHOLD;
- pstunits[i]->flags |= PST_DORESET;
- }
- }
- }
-
-
- /*
- * pst_compute_delay - compute appropriate things to tell clock about delays
- */
- void
- pst_compute_delay(prop_delay, prop_data)
- u_long prop_delay;
- struct pst_propagate *prop_data;
- {
- register int code;
- register u_long tsf;
- extern int tsftomsu();
-
- /*
- * Convert (truncate) the delay to milliseconds. Save the
- * characters needed to send this to the clock and compute
- * the remainder to be added in later.
- */
- code = tsftomsu(prop_delay, 0);
- MSUTOTSF(code, tsf);
- prop_data->remainder = prop_delay - tsf;
- if (prop_data->remainder & 0x80000000)
- prop_data->remainder = 0;
- prop_data->msbchar = BINTOPST((code >> 2) & 0x1f);
- prop_data->lsbchar = BINTOPST(code & 0x3);
- }
-
-
- /*
- * pst_control - set fudge factors, return statistics
- */
- void
- pst_control(unit, in, out)
- u_int unit;
- struct refclockstat *in;
- struct refclockstat *out;
- {
- register struct pstunit *pst;
- void pst_compute_delay();
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "pst clock: unit %d invalid (max %d)",
- unit, MAXUNITS-1);
- return;
- }
-
- if (in != 0) {
- int doqv = 0;
-
- if (in->haveflags & CLK_HAVETIME1)
- if (in->fudgetime1.l_ui == 0
- && in->fudgetime1.l_uf <= PSTMAXPROP) {
- wwv_prop_delay[unit] = in->fudgetime1;
- doqv = 1;
- pst_compute_delay(wwv_prop_delay[unit].l_uf,
- &wwv_prop_data[unit]);
- }
- if (in->haveflags & CLK_HAVETIME2)
- if (in->fudgetime2.l_ui == 0
- && in->fudgetime2.l_uf <= PSTMAXPROP) {
- wwvh_prop_delay[unit] = in->fudgetime2;
- doqv = 1;
- pst_compute_delay(wwvh_prop_delay[unit].l_uf,
- &wwvh_prop_data[unit]);
- }
- if (in->haveflags & CLK_HAVEVAL1) {
- stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
- }
- if (in->haveflags & CLK_HAVEVAL2) {
- if (in->fudgeval2 > 0 && in->fudgeval2 < 9990)
- freerun[unit] = (u_short)in->fudgeval2;
- }
- if (in->haveflags & CLK_HAVEFLAG1) {
- sloppyclock[unit] = in->flags & CLK_FLAG1;
- }
- if (unitinuse[unit]) {
- /*
- * Should actually reselect clock, but
- * will wait for the next timecode
- */
- if (in->haveflags & CLK_HAVEVAL1) {
- pstunits[unit]->peer->stratum
- = stratumtouse[unit];
- if (stratumtouse[unit] > 1)
- pstunits[unit]->peer->refid
- = htonl(PSTHSREFID);
- }
-
- if ((in->haveflags & CLK_HAVEFLAG3) &&
- (in->flags & CLK_FLAG3)) {
- pstunits[unit]->flags |= PST_DORESET;
- } else if (doqv || ((in->haveflags & CLK_HAVEFLAG2) &&
- (in->flags & CLK_FLAG2))) {
- pstunits[unit]->flags |= PST_DOQV;
- }
- }
- }
-
- if (out != 0) {
- out->type = REFCLK_WWV_PST;
- out->flags = 0;
- out->haveflags
- = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|
- CLK_HAVEVAL2|CLK_HAVEFLAG1;
- out->fudgetime1 = wwv_prop_delay[unit];
- out->fudgetime2 = wwvh_prop_delay[unit];
- out->fudgeval1 = (long)stratumtouse[unit];
- out->fudgeval2 = (long)freerun[unit];
- out->flags = sloppyclock[unit];
- if (unitinuse[unit]) {
- pst = pstunits[unit];
- out->clockdesc = pst->description;
- out->lencode = pst->lencode;
- out->lastcode = pst->lastcode;
- out->timereset = current_time - pst->timestarted;
- out->polls = pst->polls;
- out->noresponse = pst->noreply;
- out->badformat = pst->badformat;
- out->baddata = pst->baddata;
- out->lastevent = pst->lastevent;
- out->currentstatus = pst->status;
- } else {
- out->clockdesc = pstdefdesc;
- out->lencode = 0;
- out->lastcode = "";
- out->polls = out->noresponse = 0;
- out->badformat = out->baddata = 0;
- out->timereset = 0;
- out->currentstatus = out->lastevent = CEVNT_NOMINAL;
- }
- }
- }
-
-
- /*
- * pst_buginfo - return clock dependent debugging info
- */
- void
- pst_buginfo(unit, bug)
- int unit;
- register struct refclockbug *bug;
- {
- register struct pstunit *pst;
- register int i;
-
- bug->nvalues = bug->ntimes = 0;
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "pst clock: unit %d invalid (max %d)",
- unit, MAXUNITS-1);
- return;
- }
-
- if (!unitinuse[unit])
- return;
- pst = pstunits[unit];
-
- bug->nvalues = 14;
- bug->svalues = (1<<10);
- bug->values[0] = (u_long)pst->nextsample;
- bug->values[1] = (u_long)pst->state;
- bug->values[2] = (u_long)pst->reason;
- bug->values[3] = (u_long)pst->flags;
- bug->values[4] = (u_long)pst->yearday;
- bug->values[5] = (u_long)pst->hour;
- bug->values[6] = (u_long)pst->minute;
- bug->values[7] = (u_long)pst->second;
- bug->values[8] = (u_long)pst->millisecond;
- bug->values[9] = (u_long)pst->timezone;
- bug->values[10] = (u_long)((long)pst->tzoffset);
- bug->values[11] = (u_long)pst->timesincesync;
- bug->values[12] = pst->yearstart;
- if (pst->leapend > current_time)
- bug->values[13] = pst->leapend - current_time;
- else
- bug->values[13] = 0;
-
- bug->ntimes = ((NPSTSAMPS*2)+2) > NCLKBUGTIMES ? NCLKBUGTIMES :
- ((NPSTSAMPS*2)+2);
- bug->stimes = 0;
- for (i = 0; i < (bug->ntimes-2)/2; i++) {
- bug->times[2*i] = pst->rectimes[i];
- bug->times[(2*i) + 1] = pst->reftimes[i];
- }
- bug->times[bug->ntimes - 2] = pst->lastrec;
- bug->times[bug->ntimes - 1] = pst->lastref;
- }
- #endif
-